unit uDB;

interface

uses
  System.SysUtils, System.Classes, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Def,
  FireDAC.Phys, FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.VCLUI.Wait,
  FireDAC.Stan.ExprFuncs, FireDAC.Phys.SQLiteWrapper.Stat,
  FireDAC.Phys.SQLiteDef, FireDAC.Phys.SQLite, Data.DB, FireDAC.Comp.Client,
  FireDAC.Stan.Param, FireDAC.DatS, FireDAC.DApt.Intf, FireDAC.DApt,
  FireDAC.Comp.DataSet, System.Rtti, WebApi_Tool;

type
  TQuery<T> = class;

  TDB = class(TDataModule)
    Conn: TFDConnection;
    FDPhysSQLiteDriverLink1: TFDPhysSQLiteDriverLink;
    procedure DataModuleCreate(Sender: TObject);
  private
    { Private declarations }
  public
    function Query<T>: TQuery<T>;
    { Public declarations }
  end;

  TQuery<T> = class(TFDQuery)
    procedure QueryBeforePost(DataSet: TDataSet);
    procedure QueryAfterScroll(DataSet: TDataSet);
  private
    procedure getvalue;
    procedure setvalue;
    procedure Query(sql: string);
  public
    Table: T;
    procedure Where(sql: string);
    constructor Create(db: TDB);
  end;


implementation

{%CLASSGROUP 'Vcl.Controls.TControl'}

{$R *.dfm}

procedure TDB.DataModuleCreate(Sender: TObject);
begin
  Conn.Connected := true;
end;


{ TQuery }

function TDB.Query<T>: TQuery<T>;
begin
  var qry := TQuery<T>.Create(Self);
  Result := qry;
end;

{ TQuery<T> }

constructor TQuery<T>.Create(db: TDB);
begin
  inherited Create(db);
  self.Connection := db.Conn;
  self.AfterScroll := QueryAfterScroll;
  self.BeforePost := QueryBeforePost;
end;

procedure TQuery<T>.getvalue;
var
  _rtti: TRttiContext;
  tt: TRttiType;
  fields: TArray<TRttiField>;
  field: TRttiField;
  dbfield: TField;
  ty: string;
begin
  Table := Default(T);
  tt := _rtti.GetType(TypeInfo(T));
  fields := tt.GetFields;
  if (Self.Active) and (not self.IsEmpty) then
  begin
    for field in fields do
    begin
      if self.Fields.FindField(field.Name) <> nil then
      begin
        dbfield := self.FieldByName(field.Name);
        ty := field.FieldType.ToString;
        if checkType(dbfield.DataType) then
          field.SetValue(@table, dbfield.AsString)
        else if ty = 'integer' then
          field.SetValue(@table, dbfield.AsInteger)
        else if ty = 'boolean' then
          field.SetValue(@table, dbfield.AsBoolean)
        else if Pos('double,float', ty) > -1 then
          field.SetValue(@table, dbfield.AsBoolean)
      end;
    end;
  end;
end;

procedure TQuery<T>.Query(sql: string);
begin
  self.Close;
  self.SQL.Text := sql;
  self.Open;
end;

procedure TQuery<T>.QueryAfterScroll(DataSet: TDataSet);
begin
  getvalue;
end;

procedure TQuery<T>.QueryBeforePost(DataSet: TDataSet);
begin
  setvalue;
end;

procedure TQuery<T>.setvalue;
var
  _rtti: TRttiContext;
  tt: TRttiType;
  fields: TArray<TRttiField>;
  field: TRttiField;
  dbfield: TField;
  ty: string;
begin
  tt := _rtti.GetType(TypeInfo(T));
  fields := tt.GetFields;
  if (Self.Active) then
  begin
    if self.IsEmpty then
      self.Append
    else
      self.Edit;
    for field in fields do
    begin
      if self.Fields.FindField(field.Name) <> nil then
      begin
        dbfield := self.FieldByName(field.Name);
        ty := field.FieldType.ToString;
        if checkType(dbfield.DataType) then
          dbfield.AsString := field.GetValue(@table).AsString
        else if ty = 'integer' then
          dbfield.AsInteger := field.GetValue(@table).AsInteger
        else if ty = 'boolean' then
          dbfield.AsBoolean := field.GetValue(@table).AsBoolean
        else if Pos('double,float', ty) > -1 then
          dbfield.AsFloat := field.GetValue(@table).AsCurrency
      end;
    end;
  end;
end;

procedure TQuery<T>.Where(sql: string);
begin
  self.Open(sql);
end;

end.

